/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights
 * reserved. <a>http://www.cqcis.com</a>
 */
package com.cqcis.uip.base.framework.handlers.common;

import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.cqcis.uip.base.framework.FrameworkConstants;
import com.cqcis.uip.base.framework.common.InterfaceLog;
import com.cqcis.uip.base.framework.common.Role;
import com.cqcis.uip.base.framework.common.State;
import com.cqcis.uip.base.framework.common.Task;
import com.cqcis.uip.base.framework.core.Endpoint;
import com.cqcis.uip.base.framework.core.RegisteredService;
import com.cqcis.uip.base.framework.core.RegisteredServiceContext;
import com.cqcis.uip.base.framework.core.Service;
import com.cqcis.uip.base.framework.core.ServiceRole;
import com.cqcis.uip.base.framework.core.TaskResultProcessor;
import com.cqcis.uip.base.framework.handlers.CommonService;
import com.cqcis.uip.base.framework.handlers.WebServiceCaller;
import com.cqcis.uip.base.framework.service.InterfaceLogService;
import com.cqcis.uip.base.framework.service.StreamNameUtil;
import com.cqcis.uip.base.framework.service.StreamOutput;
import com.cqcis.uip.base.framework.service.impl.DefaultInterfaceLogService;
import com.cqcis.uip.base.framework.service.impl.IntfLogByteBufferAdaptor;
import com.cqcis.uip.base.framework.utils.ByteBuffer;
import com.cqcis.uip.base.framework.utils.StringUtil;
import com.cqcis.uip.base.framework.utils.WSClientThreadLocalTask;

/**
 * WebService客户端调用代理
 * 
 * @author huangmy
 * @date   2008-7-26
 */
public abstract class AbstractWebServiceCallerHandler implements CommonService, Service, TaskResultProcessor {
	private static Log log = LogFactory.getLog(AbstractWebServiceCallerHandler.class);
	public final static String WSCLIENT_BINDTASK_SOCKET = "WSCLIENT_BINDTASK_SOCKET_" + AbstractWebServiceCallerHandler.class.getName();
	public final static String WSCLIENT_SOCKET_TIMEOUT = "WSCLIENT_SOCKET_TIMEOUT_" + AbstractWebServiceCallerHandler.class.getName();
	public final static String WSCLIENT_INTF_LOG = "WSCLIENT_INTF_LOG_" + AbstractWebServiceCallerHandler.class.getName();
	private Object scheduleLock = new Object();
	private WebServiceCaller webserviceCaller; // 客户端
	private RegisteredServiceContext serviceContext;
	private StreamNameUtil streamNameUtil;
    private StreamOutput   streamOutput;
    private boolean streamRecord = true; // 是否记录二进制
    private InterfaceLogService interfaceLogService;
    private long timeout; // 超时
    private boolean keeplive; // 是否长连接
    private RegisteredService registeredService;
            
    public void start(RegisteredServiceContext serviceContext) throws Exception {
    	if (this.webserviceCaller instanceof Service) {
    		((Service)this.webserviceCaller).start(serviceContext);
    	}
    	
    	this.serviceContext = serviceContext;
    	this.registeredService = serviceContext.getRegisteredService();
    }
    
    public void stop(RegisteredServiceContext serviceContext) throws Exception {
    	if (this.webserviceCaller instanceof Service) {
    		((Service)this.webserviceCaller).stop(serviceContext);
    	}
    	
    	this.serviceContext = null;
    }
    
    public boolean processResult(Task task) {
    	if (this.webserviceCaller instanceof TaskResultProcessor) {
			return ((TaskResultProcessor)this.webserviceCaller).processResult(task);
		}
		return false;
    }
    
	public void schedule(Task task) throws Exception {	
		Task result = task.copy();
		result.setState(State.SUCCESS);
		result.setMessage(FrameworkConstants.TASK_STATE_MSG_REMOTE_DEALNG);
		result.setAttribute(FrameworkConstants.TASK_IGNORE_AFTERPROCESSRESULT, Boolean.TRUE);
		
		Endpoint fromEndpoint = task.route().from();
		RegisteredService srcRS = this.serviceContext.getServiceRepository()
		        .getRegisteredService(fromEndpoint); // 任务源服务
		if (srcRS != null) {
			result.setPriority(result.getPriority() + 1);
			srcRS.processResult(result);
		}
				
		List<IntfLogByteBufferAdaptor> records = new ArrayList<IntfLogByteBufferAdaptor>();
		task.setAttribute(WSCLIENT_INTF_LOG, records);
		task.setAttribute(WSCLIENT_SOCKET_TIMEOUT, Long.valueOf(this.timeout));
		
		synchronized (this.scheduleLock) {
			doScheduleAndRecord(task, records);
		}		
	}
	
	/**
	 * 运行
	 * 
	 * @param task
	 * @param records
	 */
	private void doScheduleAndRecord(Task task, List<IntfLogByteBufferAdaptor> records) throws Exception {
		WSClientThreadLocalTask.setTask(this.registeredService, task);		
		Exception th = null;
		try {
		    this.webserviceCaller.schedule(task); // CALL
		} catch (Exception e) {
			th = e;
			throw e;
		} finally {
			for (IntfLogByteBufferAdaptor record : records) {
				try {
					recordLog(record, th);
				} catch (Throwable t) {					
				}
			}
			
			WSClientThreadLocalTask.removeTask(this.registeredService);
			Socket socket = (Socket)task.getAttribute(AbstractWebServiceCallerHandler.WSCLIENT_BINDTASK_SOCKET);
			if (socket != null && !this.keeplive) { // 短连接, close
				try {
					socket.close();
				} catch (Exception te) {					
				}
			}
		}
	}
	
	private void recordInterfaceLog(InterfaceLog intflog, Exception th) {
    	try {
			if (intflog != null) {
				String recvRecord = intflog.getRecvRecord();
				if (!StringUtil.hasText(recvRecord) && th != null) {
					intflog.setRecvRecord(th.getMessage());
				}
				
				String sendRecord = intflog.getSendRecord();
				if (!StringUtil.hasText(sendRecord) && th != null) {
					intflog.setSendRecord(th.getMessage());
				}
				
				getInterfaceLogService().insert(intflog);
			}
		} catch (Exception e) {
			log.error("Save tin_log Exception:", e);
		}
    }
	
	/**
     * 获取interfacelog service
     * 
     * @return
     */
	private InterfaceLogService getInterfaceLogService() {
		if (this.interfaceLogService == null) {
			this.interfaceLogService = new DefaultInterfaceLogService();
		}
		
		return this.interfaceLogService;
	}
	
	/**
	 * 记录日志
	 * 
	 * @param record
	 */
	private void recordLog(IntfLogByteBufferAdaptor record, Exception th) throws Exception {
		if (!this.streamRecord) {
			return;
		}
		
		ByteBuffer sendBuffer = record.getSendBuf();
		if (sendBuffer != null) {
			sendBuffer.flip();
		}
		
		ByteBuffer recvBuffer = record.getRecvBuf();
		if (recvBuffer != null) {
			recvBuffer.flip();
		}
		
		InterfaceLog intfLog = record.getIntfLog();
		
		intfLog.setInterfaceId(this.serviceContext.getRegisteredService().getId());
				
		String filePath = streamNameUtil.getFilePath();		
		String file = null;
		String sendName = streamNameUtil.getSendName();
        if (filePath != null && sendBuffer != null && sendBuffer.hasRemaining()) {
        	file = streamOutput.write(filePath, sendName
					  , sendBuffer.buf());
        	intfLog.setSendRecord(getRecordPathFrom(file));
        }
        
        String nameSuffix = streamNameUtil.getPreFileSuffix();
        
        if (filePath != null && nameSuffix != null && recvBuffer != null && recvBuffer.hasRemaining()) {
			file = streamOutput.write(filePath,
					streamNameUtil.getRecvName(nameSuffix), recvBuffer.buf());
			intfLog.setRecvRecord(getRecordPathFrom(file));
		}
        
		recordInterfaceLog(intfLog, th);
	}
	
	/**
	 * 二进制日志路径
	 * 
	 * @param file
	 * @return
	 */
	protected String getRecordPathFrom(String file) {
		if (file == null) {
			return null;
		}
		int splitBegin = file.indexOf(FrameworkConstants.STREAM_LOG_PATH_STOKEN);
		if (splitBegin >= 0) {
			splitBegin = splitBegin + FrameworkConstants.STREAM_LOG_PATH_STOKEN.length() + 1;
		} else {
			splitBegin = 0;
		}
		
		return file.substring(splitBegin);
	}
    	
	@ServiceRole(Role.WEBSERVICE_CALLER)
	public void setWebserviceCaller(WebServiceCaller webserviceCaller) {
		this.webserviceCaller = webserviceCaller;
	}
	
	public void setInterfaceLogService(InterfaceLogService interfaceLogService) {
		this.interfaceLogService = interfaceLogService;
	}
	
	public void setStreamNameUtil(StreamNameUtil streamNameUtil) {
		this.streamNameUtil = streamNameUtil;
	}

	public void setStreamOutput(StreamOutput streamOutput) {
		this.streamOutput = streamOutput;
	}

	public void setStreamRecord(boolean streamRecord) {
		this.streamRecord = streamRecord;
	}

	public void setTimeout(long timeout) {
		this.timeout = timeout;
	}

	public void setKeeplive(boolean keeplive) {
		this.keeplive = keeplive;
	}
}
